package cn.wolfcode.wolf2w.strategy.service.impl;

import cn.wolfcode.wolf2w.common.core.constant.SystemConstants;
import cn.wolfcode.wolf2w.common.core.utils.StringUtils;
import cn.wolfcode.wolf2w.common.redis.service.RedisService;
import cn.wolfcode.wolf2w.common.redis.util.RedisKeys;
import cn.wolfcode.wolf2w.common.security.utils.SecurityUtils;
import cn.wolfcode.wolf2w.strategy.api.domain.Strategy;
import cn.wolfcode.wolf2w.strategy.api.domain.StrategyCatalog;
import cn.wolfcode.wolf2w.strategy.api.domain.StrategyCondition;
import cn.wolfcode.wolf2w.strategy.api.domain.StrategyContent;
import cn.wolfcode.wolf2w.strategy.mapper.StrategyContentMapper;
import cn.wolfcode.wolf2w.strategy.mapper.StrategyMapper;
import cn.wolfcode.wolf2w.strategy.query.StrategyQuery;
import cn.wolfcode.wolf2w.strategy.service.IStrategyService;
import cn.wolfcode.wolf2w.strategy.util.DateUtil;
import cn.wolfcode.wolf2w.strategy.util.StrategyUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * 攻略管理Service业务层处理
 *
 * @author dalang
 * @date 2023-11-23
 */
@Service
@Transactional
public class StrategyServiceImpl extends ServiceImpl<StrategyMapper, Strategy> implements IStrategyService {
    @Autowired
    private StrategyContentMapper strategyContentMapper;

    @Autowired
    @Lazy
//    private StrategyCatalogMapper strategyCatalogMapper;
    private StrategyCatalogServiceImpl strategyCatalogService;

    @Autowired
//    private StrategyThemeMapper strategyThemeMapper;
    private StrategyThemeServiceImpl strategyThemeService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    @Lazy
    private StrategyUtils strategyUtils;

    @Override
    public IPage<Strategy> queryPage(StrategyQuery qo) {
        IPage<Strategy> page = new Page<>(qo.getCurrentPage(), qo.getPageSize());
        return lambdaQuery()
                .eq(qo.getThemeId() != null, Strategy::getThemeId, qo.getThemeId())
                .eq(qo.getCatalogId() != null, Strategy::getCatalogId, qo.getCatalogId())
                .eq(qo.getIsabroad() != null, Strategy::getIsabroad, qo.getIsabroad())
                .eq(qo.getState() != null, Strategy::getState, qo.getState())
                .like(!StringUtils.isEmpty(qo.getTitle()), Strategy::getTitle, qo.getTitle())
                .eq(qo.getType() != null && qo.getType() == StrategyCondition.TYPE_THEME, Strategy::getThemeId, qo.getRefid())
                .eq(qo.getType() != null && qo.getType() != StrategyCondition.TYPE_THEME, Strategy::getDestId, qo.getRefid())
                .page(page);
    }

    @Override
    public Strategy queryById(Long id) {
        Strategy strategy = super.getById(id);
        StrategyContent strategyContent = strategyContentMapper.selectById(id);
        strategy.setContent(strategyContent);
        return strategy;
    }

    @Override
    public boolean saveOrUpdate(Strategy strategy) {
        // 目的地id，name，分类
//        StrategyCatalog catalog = strategyCatalogMapper.selectById(strategy.getCatalogId());
        StrategyCatalog catalog = strategyCatalogService.getById(strategy.getCatalogId());
        strategy.setDestId(catalog.getDestId());
        strategy.setDestName(catalog.getDestName());
        strategy.setCatalogName(catalog.getName());
        // 攻略主题name
//        strategy.setThemeName(strategyThemeMapper.selectById(strategy.getThemeId()).getName());
        strategy.setThemeName(strategyThemeService.getById(strategy.getThemeId()).getName());

        boolean flag = false;
        if (strategy.getId() == null) {
            strategy.setCreateTime(new Date());
            strategy.setViewnum(0);
            strategy.setReplynum(0);
            strategy.setFavornum(0);
            strategy.setSharenum(0);
            strategy.setThumbsupnum(0);
            boolean save = super.save(strategy);
            StrategyContent strategyContent = strategy.getContent();
            strategyContent.setId(strategy.getId());
            int insert = strategyContentMapper.insert(strategyContent);
            flag = save && insert > 0;
        } else {
            boolean update = super.updateById(strategy);
            StrategyContent strategyContent = strategy.getContent();
            strategyContent.setId(strategy.getId());
            int updateCount = strategyContentMapper.updateById(strategyContent);
            flag = update && updateCount > 0;
        }
        return flag;
    }

    @Override
    public List<Strategy> queryByCatalogId(Long cid) {
        List<Strategy> list = lambdaQuery().eq(Strategy::getCatalogId, cid).list();
        return list;
    }

    @Override
    public StrategyContent getContent(Long id) {
        List<Strategy> Strategies = lambdaQuery()
                .eq(Strategy::getCatalogId, id)
                .orderByAsc(Strategy::getId)
                .list();
//        List<Long> ids = Strategies.stream().map(Strategy::getId).collect(Collectors.toList());
        return strategyContentMapper.selectById(Strategies.get(0).getId());
    }

    @Override
    public Strategy viewnumUp(Long sid) {
        Strategy strategy = super.getById(sid);
        strategy.setViewnum(strategy.getViewnum() + 1);
        super.updateById(strategy);
        return strategy;
    }

    @Override
    public List<Strategy> viewnnumTop3(Long destId) {
        return lambdaQuery().eq(Strategy::getDestId, destId)
                .orderByDesc(Strategy::getViewnum)
                .last("limit 3")
                .list();
    }

    @Override
    public Map<String, Object> viewnumIncr(Long sid) {
        return this.numIncr(sid, "viewnum", 1L);
    }

    @Override
    public Map<String, Object> replynumIncr(Long sid) {
        return this.numIncr(sid, "replynum", 1L);
    }

    @Override
    public Map<String, Object> sharenumIncr(Long sid) {
        return this.numIncr(sid, "sharenum", 1L);
    }

    @Override
    public Map<String, Object> favor(Long sid) {
        Long uid = SecurityUtils.getUserId();
        String key = RedisKeys.USER_FAVOR.join(uid.toString());
        if (!redisService.hasKey(key)) {
            Set<Long> set = new HashSet<>();
            set.add(-1L);
            redisService.setCacheSet(key, set);
        }

        boolean flag = false;
        if (redisService.isCacheSetContains(key, sid)) {
            this.numIncr(sid, "favornum", -1L);
            redisService.deleteCacheSetValue(key, sid);
        } else {
            this.numIncr(sid, "favornum", 1L);
            redisService.addCacheSetValue(key, sid);
            flag = true;
        }
        String skey = RedisKeys.STRATEGY_HASH.join(sid.toString());
        Map<String, Object> smap = redisService.getCacheMap(skey);
        smap.put("result", flag);

        return smap;
    }

    @Override
    public Map<String, Object> thumbsup(Long sid) {
        Long uid = SecurityUtils.getUserId();
        String key = RedisKeys.STRATEGY_THUMB.join(sid.toString(), uid.toString());
        boolean flag = false;

        if (!redisService.hasKey(key)) {
            Date now = new Date();
            Date end = DateUtil.getEndDate(now);
            Long time = DateUtil.getDateBetween(now, end);
            redisService.setCacheObject(key, "thumbsup", time, TimeUnit.SECONDS);
            this.numIncr(sid, "thumbsupnum", 1L);
            flag = true;
        }
        String skey = RedisKeys.STRATEGY_HASH.join(sid.toString());
        Map<String, Object> map = redisService.getCacheMap(skey);
        map.put("result", flag);
        return map;
    }

    @Override
    public Boolean isUserFavor(Long sid, Long uid) {
        String key = RedisKeys.USER_FAVOR.join(uid.toString());
        boolean flag = redisService.isCacheSetContains(key, sid);
        return flag;
    }

    @Override
    public void updateCache() {
//        Collection<String> keys = redisService.keys(RedisKeys.STRATEGY_HASH.join("*"));
        List<String> keys = this.redisScan(SystemConstants.STRATEGY_HASH_PREFIX + ":*");
        if (keys != null && keys.size() > 0) {
            for (String key : keys) {
//            Long sid = Long.parseLong(RedisKeys.STRATEGY_HASH.getSuffix(key));
                Map<String, Object> map = redisService.getCacheMap(key);
//                Long sid = Long.parseLong(map.get("strategyId").toString());
//                LambdaUpdateWrapper<Strategy> wrapper = new LambdaUpdateWrapper<>();
                lambdaUpdate().eq(Strategy::getId, map.get("strategyId"))
                        .set(Strategy::getViewnum, map.get("viewnum"))
                        .set(Strategy::getReplynum, map.get("replynum"))
                        .set(Strategy::getFavornum, map.get("favornum"))
                        .set(Strategy::getSharenum, map.get("sharenum"))
                        .set(Strategy::getThumbsupnum, map.get("thumbsupnum"))
                        .update();
//                super.update(wrapper);
            }
        }

    }

    /**
     * scan 实现
     *
     * @param pattern  表达式
     * @param consumer 对迭代到的key进行操作
     */
    public void scan(String pattern, Consumer<byte[]> consumer) {
        redisTemplate.execute((RedisConnection connection) -> {
            try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().count(Long.MAX_VALUE).match(pattern).build())) {
                cursor.forEachRemaining(consumer);
                return null;
            } catch (RuntimeException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 获取符合条件的key
     *
     * @param pattern 表达式
     * @return
     */
    public List<String> redisScan(String pattern) {
        List<String> keys = new ArrayList<>();
        this.scan(pattern, item -> {
            //符合条件的key
            String key = new String(item, StandardCharsets.UTF_8);
            keys.add(key);
        });
        return keys;
    }

    @Override
    public String initStrategyHash(Long sid) {
        String key = RedisKeys.STRATEGY_HASH.join(sid.toString());

        if (!redisService.hasKey(key)) {
            Strategy strategy = super.getById(sid);
            HashMap<String, Object> map = new HashMap<>();
            map.put("viewnum", strategy.getViewnum());
            map.put("replynum", strategy.getReplynum());
            map.put("favornum", strategy.getFavornum());
            map.put("sharenum", strategy.getSharenum());
            map.put("thumbsupnum", strategy.getThumbsupnum());
            map.put("strategyId", strategy.getId());

            redisService.setCacheMap(key, map);
        }

        return key;
    }

    private Map<String, Object> numIncr(Long sid, String field, Long num) {
        String key = initStrategyHash(sid);

        redisService.incrementCacheMapValue(key, field, num);
        return redisService.getCacheMap(key);
    }
}
